package com.i72.freeway;

import lombok.extern.slf4j.Slf4j;

import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.*;
import java.util.regex.Pattern;
import java.util.zip.Deflater;
import java.util.zip.Inflater;

/**
 * @author jiangj
 * @version 1.0.0
 * @ClassName StringHelper.java
 * @Description TODO
 * @createTime 2021年12月28日 15:51:00
 */
@Slf4j
public class StringHelper {

    public static final String[]        exprSpecialWords        = { "\\", "$", "(", ")", "*", "+", ".", "[", "]", "?", "^", "{", "}", "|" };
    public static final String[]        replacementSpecialWords = { "\\", "$" };
    private static Map<String, Field[]> fieldMap                = new HashMap<>();

    /**
     * 判断字符串是否为空
     * @param source 待判断的字符串
     * @return true:为空 false：不为空
     * @author 刘海峰
     * @since 2018/3/26
     * <p>
     * <strong>示例一：</strong>
     * </p>
     * <blockquote>
     *
     * <pre>
     * {@code
     *      public void isNullOrEmpty(){
     *          boolean result = StringHelper.isNullOrEmpty("");//result:true
     *          result = StringHelper.isNullOrEmpty("    ");//result:true
     *          result = StringHelper.isNullOrEmpty(null);//result:true
     *          result = StringHelper.isNullOrEmpty("222");//result:false
     *      }
     * }
     * </pre>
     *
     * </blockquote>
     **/
    public static boolean isNullOrEmpty(String source) {
        return source == null || source.trim().isEmpty();
    }

    /**
     * 判断对象是否为空字符串
     * @param source 待判断的Object类型的值(暂时只支持简单类型和简单类型的封装类型以及字符串)
     * @return boolean true:为空 false:不为空
     * @author 刘海峰
     * @since 2018/3/26
     * <p>
     * <strong>示例一：</strong>
     * </p>
     * <blockquote>
     *
     * <pre>
     * {@code
     *         public void isObjectNullOrEmpty{
     *             boolean result = StringHelper.isObjectNullOrEmpty(null);//result:true
     *
     *             result = StringHelper.isObjectNullOrEmpty("");//result:true
     *
     *             result = StringHelper.isObjectNullOrEmpty("  ");//result:false
     *
     *             result = StringHelper.isObjectNullOrEmpty("kkk");//result:false
     *
     *             result = StringHelper.isObjectNullOrEmpty(1);//result:false
     *
     *             StringHelper.isObjectNullOrEmpty('o');//result:false
     *         }
     *     }
     * </pre>
     *
     * </blockquote>
     **/
    public static boolean isObjectNullOrEmpty(Object source) {
        return source == null || "".equals(source.toString().trim());
    }

    /**
     * 首字母转小写
     * @param s 待转换对象（String类型，不可为空）
     * @return 转换结果
     * @author 陈嘉文
     * @since 2018/3/26
     * <p>
     * <strong>示例一：</strong>
     * </p>
     * <blockquote>
     *
     * <pre>
     * {@code
     *         public void toLowerCaseFirstOne{
     *             String result = StringHelper.toLowerCaseFirstOne("AbC");//result:"abC"
     *         }
     *     }
     * </pre>
     *
     * </blockquote>
     */
    public static String toLowerCaseFirstOne(String s) {
        if (StringHelper.isNullOrEmpty(s)) return s;
        if (Character.isLowerCase(s.charAt(0))) return s;
        return Character.toLowerCase(s.charAt(0)) + s.substring(1);
    }

    /**
     * 首字母转大写
     */
    public static String toUpperCaseFirstOne(String s) {
        if (StringHelper.isNullOrEmpty(s)) return s;
        if (Character.isUpperCase(s.charAt(0))) return s;
        return Character.toUpperCase(s.charAt(0)) + s.substring(1);
    }

    /**
     * 替换忽略大小写
     * @param source 初始字符串
     * @param replacement 待替换的字符串
     * @param target 替换目标字符串
     * @return String 替换后的字符串,并且全部转换为大写字母
     * @author 陈嘉文
     * @since 2018/3/26
     * <p>
     * <strong>示例一：</strong>
     * </p>
     * <blockquote>
     *
     * <pre>
     * {@code
     *         public void replaceIgnoreCase{
     *             String result = StringHelper.replaceIgnoreCase("abcde","c","BD"); //result:"abBDde"
     *             result = StringHelper.replaceIgnoreCase("abcde","f","BD");//result:"abcde"
     *             result = StringHelper.replaceIgnoreCase("abcbe","b","BD");//result:"aBDcBDe"
     *
     *             result = StringHelper.replaceIgnoreCase(null,"c","BD");//result:null
     *             result = StringHelper.replaceIgnoreCase("abcbe","b","  ");//result:"a  c  e"
     *         }
     *     }
     * </pre>
     *
     * </blockquote>
     */
    public static String replaceIgnoreCase(String source, String target, String replacement) {
        if (isNullOrEmpty(source) || target == null || target.isEmpty() || replacement == null) {
            return source;
        }

        return source.replaceAll("(?i)" + escapeExprSpecialWord(target, exprSpecialWords), escapeExprSpecialWord(replacement, replacementSpecialWords));
    }

    /**
     * 转义正则特殊字符 （$()*+.[]?\^{},|） \\需要第一个替换，否则replace方法替换时会有逻辑bug
     * @param value 内容
     * @return 替换之后的内容
     * @author 余焕
     * @since 2018/8/9
     */
    public static String escapeExprSpecialWord(String value) {
        return escapeExprSpecialWord(value, exprSpecialWords);
    }

    /**
     * 转义正则特殊字符 （$()*+.[]?\^{},|） \\需要第一个替换，否则replace方法替换时会有逻辑bug
     * @param value 内容
     * @return 替换之后的内容
     * @author 余焕
     * @since 2018/8/9
     */
    public static String escapeExprSpecialWord(String value, String[] specialWords) {
        if (!isNullOrEmpty(value)) {
            for (String key : specialWords) {
                if (value.contains(key)) {
                    value = value.replace(key, "\\" + key);
                }
            }
        }
        return value;
    }

    /**
     * 解压zlib 压缩后的字符串
     * @param data zlib压缩后的字符串
     * @return byte[] 解压后的结果
     * @author 陈嘉文
     * @since 2018/3/26
     * <p>
     * <strong>示例一:</strong>
     * </p>
     * <blockquote>
     *
     * <pre>
     * {@code
     *          public void decompress(){
     *              //result:{120,-100,43,73,45,46,81,72,-50,-49,45,40,74,45,46,86,40,46,41,-54,-52,75,7,0,83,-84,84}
     *              byte [] data = StringHelper.compress("test compress string");
     *
     *              //result:{116,101,115,116,32,99,111,109,112,114,101,115,115,32,115,116,114,105,110,103}
     *              byte[]  result = StringHelper.decompress(data);
     *          }
     *     }
     * </pre>
     *
     * </blockquote>
     */
    public static byte[] decompress(byte[] data) {
        byte[] output;
        Inflater decompresser = new Inflater();
        decompresser.reset();
        decompresser.setInput(data);
        try (ByteArrayOutputStream bos = new ByteArrayOutputStream(data.length)) {
            byte[] buf = new byte[1024];
            while (!decompresser.finished()) {
                int i = decompresser.inflate(buf);
                if (i == 0) {
                    break;
                }
                bos.write(buf, 0, i);
            }
            output = bos.toByteArray();
        } catch (Exception e) {
            output = data;
            log.error("decompress failure", e);
        } finally {
            decompresser.end();
        }
        return output;
    }

    /**
     * 以zlib的格式压缩字符串，输出成byte数组
     * @param str 待压缩字符串
     * @return byte[]
     * @author 陈嘉文
     * @since 2018/3/26
     * <p>
     * <strong>示例一:</strong>
     * </p>
     * <blockquote>
     *
     * <pre>
     * {@code
     *          public void compress(){
     *
     *              //result:{120,-100,43,73,45,46,81,72,-50,-49,45,40,74,45,46,86,40,46,41,-54,-52,75,7,0,83,-84,84}
     *              byte [] result = StringHelper.compress("test compress string");
     *
     *              //result:{120,-100,1,21,0,-22,-1,-26,-117,-24,-81,-107,-27,-114,-117,-25,-68,-87,-27,-83,-105,-25,-84,-90,-28,-72,-78,-88,41,15,77}
     *              result = StringHelper.compress("测试压缩字符串");
     *          }
     *     }
     * </pre>
     *
     * </blockquote>
     */
    public static byte[] compress(String str) {
        byte[] data = str.getBytes();
        byte[] output;
        Deflater compresser = new Deflater();

        compresser.reset();
        compresser.setInput(data);
        compresser.finish();
        try (ByteArrayOutputStream bos = new ByteArrayOutputStream(data.length)) {
            byte[] buf = new byte[1024];
            while (!compresser.finished()) {
                int i = compresser.deflate(buf);
                if (i == 0) {
                    break;
                }
                bos.write(buf, 0, i);
            }
            output = bos.toByteArray();
        } catch (Exception e) {
            output = data;
            log.error("compress failure", e);
        } finally {
            compresser.end();
        }
        return output;
    }

    /*
     * 判断是否为整数
     * @param str 传入的字符串
     * @return 是整数返回true,否则返回false
     * @author yuhuan
     * @since 2018/8/6
     */
    public static boolean isInteger(String str) {
        if (isNullOrEmpty(str)) return false;

        Pattern pattern = Pattern.compile("^[-+]?[\\d]*$");
        return pattern.matcher(str).matches();
    }

    /*
     * 字符串转换成整形
     * @param str 传入的字符串
     * @return 返回null说明传入的字符串不是整形
     * @author yuhuan
     * @since 2018/12/28
     */
    public static Integer convertToInteger(String str) {
        if (!isInteger(str)) return null;
        return Integer.parseInt(str);
    }

    /**
     * 保留字符串的头部和尾部，中间部分有......替换
     * @param source 源字符串
     * @param handlerLength 超出该长度则处理
     * @param keepLength 保留头部和尾部的长度
     * @return 结果
     * @author yuhuan
     * @since 2018/8/17
     */
    public static String getHeadAndTail(String source, int handlerLength, int keepLength) {
        if (!StringHelper.isNullOrEmpty(source) && source.length() > handlerLength && handlerLength >= keepLength * 2) {
            int length = source.length();
            return source.substring(0, keepLength) + "......" + source.substring(length - keepLength, length);
        }
        return source;
    }

    /**
     * 对sql特殊字符单引号 ' 转义
     * @param str sql
     * @return 转义之后的sql
     */
    @Deprecated
    public static String escapeSql(String str) {
        if (!isNullOrEmpty(str)) {
            return str.replaceAll("'", "''");
        }
        return str;
    }

    /**
     * 判断对象是否所有属性为空
     * @param entity 对象
     * @return true or false
     */
    public static boolean isEmptyEntity(Object entity) {
        if (isObjectNullOrEmpty(entity)) {
            return true;
        }

        try {
            Field[] fields = fieldMap.get(entity.getClass().getName());
            if (Objects.isNull(fields)) {
                fields = entity.getClass().getDeclaredFields();
                List<Field> fieldList = new ArrayList<>(Arrays.asList(fields));

                Class<?> superclass = entity.getClass().getSuperclass();
                while (superclass != null && !"java.lang.Object".equals(superclass.getName())
                        && !"com.swj.basic.dto.BasePageParam".equals(superclass.getName())) {
                    fields = superclass.getDeclaredFields();
                    fieldList.addAll(Arrays.asList(fields));

                    superclass = superclass.getSuperclass();
                }
                fieldMap.put(entity.getClass().getName(), fieldList.toArray(new Field[0]));
            }

            for (Field field : fieldMap.get(entity.getClass().getName())) {
                field.setAccessible(true);
                Object o = field.get(entity);
                if (null == o) {
                    continue;
                }

                boolean isCollection = o instanceof Collection;

                /*
                if (isCollection && !ListHelper.isNullOrEmpty((Collection<?>) o)) {
                    return false;
                }*/
                if(isCollection && (o!=null && !((Collection<?>) o).isEmpty())){
                    return false;
                }

                if (!isCollection && !o.toString().trim().isEmpty()) {
                    return false;
                }
            }
        } catch (Exception e) {
            log.error("not blank validator failed", e);
        }

        return true;
    }

    /**
     * 替换字符串最后出现的元素
     * @param text 源字符串
     * @param regex 正则表达式
     * @param replacement 替换成的字符串
     * @return 替换之后的字符串
     */
    public static String replaceLast(String text, String regex, String replacement) {
        return text.replaceFirst("(?s)" + escapeExprSpecialWord(regex, exprSpecialWords) + "(?!.*?" + escapeExprSpecialWord(regex, exprSpecialWords) + ")",
                escapeExprSpecialWord(replacement, replacementSpecialWords));
    }

    /**
     * 字符串trim
     */
    public static String trim(Object object) {
        return object == null ? null : object.toString().trim();
    }

    /**
     * url编码
     * @param str 编码的字符串
     * @return 编码之后的字符串
     */
    public static String URLEncode(String str) {
        if (isNullOrEmpty(str)) return str;
        try {
            return URLEncoder.encode(str, "UTF-8");
        } catch (UnsupportedEncodingException ignored) {}
        return null;
    }

    /**
     * url解码
     * @param str 解码的字符串
     * @return 解码之后的字符串
     */
    public static String URLDecode(String str) {
        if (isNullOrEmpty(str)) return str;
        try {
            return URLDecoder.decode(str, "UTF-8");
        } catch (UnsupportedEncodingException ignored) {}
        return null;
    }

    /**
     * 移除字符串结尾字符
     * @param str 源字符串
     * @param remove 需移除的字符
     * @return 结果
     */
    public static String removeEnd(String str, String remove) {
        if (isNullOrEmpty(str) || isNullOrEmpty(remove)) return str;
        return str.endsWith(remove) ? str.substring(0, str.length() - remove.length()) : str;
    }

    /**
     * 解析配置值，返回Map。默认用英文分号;分隔，若值里面;不属于分隔符，使用反斜杠\进行转义
     * @param value 格式：key1=value1;key2=value2。
     */
    public static Map<String, String> parseConfigValue(String value) {
        return parseConfigValue(value, ";");
    }

    /**
     * 解析配置值，返回Map.若值里面有分隔符，使用反斜杠\进行转义
     * @param value 格式：key1=value1(splitChar)key2=value2
     * @param splitChar 分隔符号
     */
    public static Map<String, String> parseConfigValue(String value, String splitChar) {
        Map<String, String> result = new HashMap<>();
        if (isNullOrEmpty(value)) {
            return result;
        }

        String[] keyAndValues = value.split("(?<!\\\\)" + escapeExprSpecialWord(splitChar));
        for (String keyAndValue : keyAndValues) {
            String[] array = keyAndValue.split("=", 2);
            result.put(array[0].trim(), array[1].trim().replaceAll("\\\\" + escapeExprSpecialWord(splitChar), splitChar));
        }
        return result;
    }

    /**
     * 将驼峰式命名的字符串转换为下划线小写方式。如果转换前的驼峰式命名的字符串为空，则返回源字符串。
     * 例如：HelloWorld->hello_world
     * @param name 转换前的驼峰式命名的字符串
     * @return 转换后下划线小写方式命名的字符串
     */
    public static String underscoreName(String name) {
        if (name == null || name.length() <= 1) {
            return name;
        }

        StringBuilder result = new StringBuilder();
        char[] chars = name.toCharArray();
        result.append(Character.toLowerCase(chars[0]));
        for (int i = 1; i < chars.length; i++) {
            if (Character.isUpperCase(chars[i])) {
                result.append("_").append(Character.toLowerCase(chars[i]));
            } else {
                result.append(chars[i]);
            }
        }
        return result.toString();
    }

    /**
     * 将下划线命名的字符串转换为驼峰式。如果转换前的下划线命名的字符串为空，则返回源字符串。
     * 例如：HELLO_WORLD->HelloWorld
     * @param name 转换前的下划线命名的字符串
     * @return 转换后的驼峰式命名的字符串
     */
    public static String camelName(String name) {
        if (name == null || name.length() <= 1 || !name.contains("_")) {
            return name;
        }

        StringBuilder result = new StringBuilder();
        // 用下划线将原始字符串分割
        String[] camels = name.split("_");
        for (String camel : camels) {
            // 跳过原始字符串中开头、结尾的下换线或双重下划线
            if (camel.isEmpty()) {
                continue;
            }
            // 处理真正的驼峰片段
            result.append(camel.substring(0, 1).toUpperCase());
            result.append(camel.substring(1).toLowerCase());
        }
        return result.toString();
    }

    public static String objectToString(Object object) {
        if (object == null) {
            return null;
        }

        String result = null;
        if (object.getClass().isArray()) {
            int length = Array.getLength(object);

            int iMax = length - 1;
            if (iMax == -1) return "[]";

            StringBuilder b = new StringBuilder();
            b.append('[');
            for (int i = 0; i < length; i++) {
                b.append(Array.get(object, i));
                if (i == iMax) {
                    result = b.append(']').toString();
                } else {
                    b.append(", ");
                }

            }
        } else {
            result = object.toString();
        }
        return result;
    }

}
